package cn.autumnorange.app.common.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.IOException;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;

//   我本地windows系统上部署的redis集群，配置在项目中，启动项目报错如标题所示。排查问题，首先，连上我的主节点：redis-cli  -h  127.0.0.1 -p 6379
// ，可以正常连上，测试存取状态，set test 123 报错： [ERR] clusterdown the cluster is down。貌似主节点出问题了。
//
//        用命令检测一下试试：redis-trib.rb check 127.0.0.1:6379
//
//        检测结果： [ERR] Not all 16384 slots are covered by nodes。
//
//        执行修复命令：redis-trib.rb fix 127.0.0.1:6379。
//        ————————————————
//        原文链接：https://blog.csdn.net/u014034329/article/details/83833665
// 我本地windows系统上部署的redis集群，配置在项目中，启动项目报错如标题所示。排查问题，首先，连上我的主节点：redis-cli  -h  127.0.0.1 -p 6379
// ，可以正常连上，测试存取状态，set test 123 报错： [ERR] clusterdown the cluster is down。貌似主节点出问题了。
//
//        用命令检测一下试试：redis-trib.rb check 127.0.0.1:6379
//
//        检测结果： [ERR] Not all 16384 slots are covered by nodes。
//
//        执行修复命令：redis-trib.rb fix 127.0.0.1:6379。
// ruby redis-trib.rb create --replicas 1 127.0.0.1:6379 127.0.0.1:7001 127.0.0.1:7002
// 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005
// https://github.com/LX1309244704/SpringBoot-master/blob/master/springboot-redis/springboot-redis-service/src/main/java/com/redis/config/RedisConfig.java
// https://blog.csdn.net/lx1309244704/article/details/80696235
// 连接池注入配置信息
@Configuration
@EnableCaching // 开启缓存支持
public class RedisConfig extends CachingConfigurerSupport {

  //    @Bean
  //    public LettuceConnectionFactory lettuceConnectionFactory() {
  //        List<String> nodeList = new ArrayList<>();
  //
  //        nodeList.add("127.0.0.1:6379");
  //        nodeList.add("127.0.0.1:7001");
  //        nodeList.add("127.0.0.1:7002");
  //        nodeList.add("127.0.0.1:7003");
  //        nodeList.add("127.0.0.1:7004");
  //        nodeList.add("127.0.0.1:7005");
  //
  //        redisConfigurationProperties.setNodes(nodeList);
  ////        redisConfigurationProperties.setMaxRedirects(5);
  //        Map<String, Object> source = new HashMap<>();
  //        source.put("spring.redis.cluster.nodes",
  // "127.0.0.1:6379,127.0.0.1:7001,127.0.0.1:7002,127.0.0.1:7003,127.0.0.1:7004,127.0.0.1:7005");
  //        source.put("spring.redis.cluster.max-redirects", 5);
  //        RedisClusterConfiguration redisClusterConfiguration;
  //        redisClusterConfiguration = new RedisClusterConfiguration(new
  // MapPropertySource("RedisClusterConfiguration", source));
  //        return new LettuceConnectionFactory(redisClusterConfiguration);
  //    }

  @Bean
  public RedissonClient redisson() throws IOException {
    Config config = Config.fromYAML(new ClassPathResource("redisson.yml").getInputStream());
    //    config.useClusterServers();
    RedissonClient redisson = Redisson.create(config);
    return redisson;
  }

  @Bean
  public RedisConnectionFactory redissonConnectionFactory(RedissonClient redisson) {
    return new RedissonConnectionFactory(redisson);
  }

  @Autowired private RedisConnectionFactory redisConnectionFactory;
  //  @Autowired private RedisConnectionFactory lettuceConnectionFactory;
  @Bean
  public KeyGenerator keyGenerator() {
    return (target, method, params) -> {
      StringBuilder sb = new StringBuilder();
      sb.append(target.getClass().getName());
      sb.append(method.getName());
      for (Object obj : params) {
        // 由于参数可能不同, 缓存的key也需要不一样
        sb.append(obj.toString());
      }
      return sb.toString();
    };
  }

  // 缓存管理器
  @Override
  @Bean
  public CacheManager cacheManager() {
    // 设置序列化
    RedisSerializer<String> stringSerializer = new StringRedisSerializer();
    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
        new Jackson2JsonRedisSerializer<Object>(Object.class);
    RedisCacheConfiguration config =
        RedisCacheConfiguration.defaultCacheConfig()
            .serializeKeysWith(
                RedisSerializationContext.SerializationPair.fromSerializer(stringSerializer))
            // value序列化方式
            .serializeValuesWith(
                RedisSerializationContext.SerializationPair.fromSerializer(
                    jackson2JsonRedisSerializer))
            .disableCachingNullValues()
            // 缓存过期时间
            .entryTtl(Duration.ofMinutes(5));

    RedisCacheManager.RedisCacheManagerBuilder builder =
        RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(redisConnectionFactory)
            .cacheDefaults(config)
            .transactionAware();
    @SuppressWarnings("serial")
    Set<String> cacheNames =
        new HashSet<String>() {
          {
            add("codeNameCache");
          }
        };
    builder.initialCacheNames(cacheNames);
    return builder.build();
  }

  //    原文链接：https://blog.csdn.net/lx1309244704/article/details/80696235
  //    public Object getFieldValueByObject(Object object, String targetFieldName) throws Exception
  // {
  //        // 获取该对象的Class
  //        Class objClass = object.getClass();
  //        // 获取所有的属性数组
  //        Field[] fields = objClass.getDeclaredFields();
  //        for (Field field : fields) {
  //            // 属性名称
  //            field.setAccessible(true);
  //            String currentFieldName = field.getName();
  //            if (currentFieldName.equals(targetFieldName)) {
  //                return field.get(object); // 通过反射拿到该属性在此对象中的值(也可能是个对象)
  //            }
  //        }
  //        return null;
  //    }
  //
  //    /**
  //     * 通过反射获取JedisCluster
  //     *
  //     * @param factory
  //     * @return
  //     */
  //    @Bean
  //    public JedisCluster redisCluster(RedisConnectionFactory factory) {
  //        Object object = null;
  //        try {
  //            object = getFieldValueByObject(factory, "cluster");
  //        } catch (Exception e) {
  //            e.printStackTrace();
  //        }
  //        return (JedisCluster) object;
  //
  //    }

  //    @Bean
  //    public JedisCluster jedisCluster() {
  //        Set<HostAndPort> nodeSet = new HashSet<>();
  //        for (String node : redisConfigurationProperties.getNodes()) {
  //            String[] split = node.split(":");
  //            nodeSet.add(new HostAndPort(split[0], Integer.valueOf(split[1])));
  //        }
  //        return new JedisCluster(nodeSet);
  //    }

  //    @Autowired
  //    private DataSource dataSource;
  //
  // 配置事务管理器
  //    @Bean
  //    public PlatformTransactionManager transactionManager(DataSource dataSource) throws
  // SQLException {
  //        return new DataSourceTransactionManager(dataSource);
  //    }

  //    @Bean
  //    public DataSource dataSource() {
  //
  //        return DruidDataSourceBuilder.create().build();
  //    }
  // 因为redis是一种基于字符串存储的NoSQL，而Java是一种基于对象的语言，对象是无法直接存储到redis的，所以可以通过序列化的方式，将对象转为二进制字符串存储，同时通过反序列化转为对象。
  @Bean
  @Primary
  public RedisTemplate<String, Object> redisTemplate() {
    System.out.println("redisConnectionFactory : " + (redisConnectionFactory.getClass().getName()));
    // 设置序列化
    // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值（默认使用JDK的序列化方式）
    Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer =
        new Jackson2JsonRedisSerializer(Object.class);
    ObjectMapper om = new ObjectMapper();
    // 指定要序列化的域，field,get和set,以及修饰符范围，ANY是都有包括private和public
    om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
    om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    jackson2JsonRedisSerializer.setObjectMapper(om);
    // 配置redisTemplate
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
    // 配置连接工厂
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    RedisSerializer<?> stringSerializer = new StringRedisSerializer();
    redisTemplate.setKeySerializer(stringSerializer); // key序列化
    redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); // value序列化
    redisTemplate.setHashKeySerializer(stringSerializer); // Hash key序列化
    redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); // Hash value序列化
    redisTemplate.setEnableTransactionSupport(false);
    redisTemplate.afterPropertiesSet();
    return redisTemplate;
  }

  /**
   * @return
   * @author 七脉 描述：需要手动注册RedisMessageListenerContainer加入IOC容器
   */
  @Bean
  public RedisMessageListenerContainer redisMessageListenerContainer() {

    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(redisConnectionFactory);
    return container;
  }
}
